#!/usr/bin/env python
# -*- coding:utf-8 -*-
import os
import uuid
from datetime import datetime
from typing import Dict
from pptx import Presentation
from werkzeug.datastructures import FileStorage
from pdf2image import convert_from_path  # 用于处理 PDF 转图片
from controller.templateController import TemplateController
from models import db
from models.template import Template
from utils.loggings import loggings
from utils.generate_id import GenerateID
from utils.response_code import RET
from io import BytesIO
from flask import send_file, current_app
from werkzeug.utils import secure_filename


class TemplateService(TemplateController):
    upload_dir = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'uploads', 'resource')
    # 定义相对路径的uploads目录
    upload_base = os.path.join('uploads', 'templates')
    base_dir = os.path.dirname(os.path.dirname(__file__))

    @classmethod
    def add_ppt_template(cls, **kwargs: dict) -> dict:
        """
        添加 PPT 模板
        :param kwargs: 包含 user_id, template_name, file, cover_pic（可选）的参数字典
        :return: 结果字典
        """
        try:
            user_id = kwargs.get('user_id')
            template_name = kwargs.get('template_name')
            ppt_file: FileStorage = kwargs.get('file')
            cover_pic: str = kwargs.get('cover_pic', "")

            if not user_id or not template_name or not ppt_file:
                return {'code': RET.PARAMERR, 'message': "缺少必要参数", 'data': {}}

            # 创建目录
            user_template_dir = os.path.join(cls.upload_base, str(user_id))
            os.makedirs(user_template_dir, exist_ok=True)

            # 生成唯一模板ID及保存路径
            template_id = GenerateID.create_random_id()
            relative_path = os.path.join(user_template_dir, f"{template_id}.pptx")
            ppt_file.save(relative_path)

            # 插入数据库记录
            new_template = Template(
                template_id=template_id,
                user_id=user_id,
                cover_pic=cover_pic,
                location=relative_path,  # 相对路径存储
                is_system=0,
                is_deleted=0,
                template_name=template_name,
            )
            db.session.add(new_template)
            db.session.commit()

            return {'code': RET.OK, 'message': "模板添加成功", 'data': {'template_id': template_id}}

        except Exception as e:
            loggings.exception(f"Error in add_ppt_template: {e}")
            db.session.rollback()
            return {'code': RET.SYSERR, 'message': "系统异常，请稍后再试", 'data': {'error': str(e)}}

    @classmethod
    def update_ppt_template(cls, **kwargs: dict) -> dict:
        """
        更新 PPT 模板
        :param kwargs: 包含 user_id, template_id, template_name, file, cover_pic（可选）的参数字典
        :return: 结果字典
        """
        try:
            user_id = kwargs.get('user_id')
            template_id = kwargs.get('template_id')
            template_name = kwargs.get('template_name')
            ppt_file: FileStorage = kwargs.get('file')
            cover_pic: str = kwargs.get('cover_pic', "")

            if not user_id or not template_id or not template_name or not ppt_file:
                return {'code': RET.PARAMERR, 'message': "缺少必要参数", 'data': {}}

            # 查询数据库中是否存在模板
            existing_template = Template.query.filter_by(
                user_id=user_id,
                template_id=template_id,
                is_deleted=0
            ).first()
            if not existing_template:
                return {'code': RET.NODATA, 'message': "模板不存在或已删除", 'data': {}}

            # 更新PPT文件
            user_template_dir = os.path.join(cls.upload_base, str(user_id))
            os.makedirs(user_template_dir, exist_ok=True)
            relative_path = os.path.join(user_template_dir, f"{template_id}.pptx")
            ppt_file.save(relative_path)

            # 更新数据库记录
            existing_template.template_name = template_name
            existing_template.location = relative_path  # 相对路径
            if cover_pic:
                existing_template.cover_pic = cover_pic
            existing_template.create_time = datetime.now()

            db.session.commit()
            return {'code': RET.OK, 'message': "模板更新成功", 'data': {'template_id': template_id}}

        except Exception as e:
            loggings.exception(f"Error in update_ppt_template: {e}")
            db.session.rollback()
            return {'code': RET.SYSERR, 'message': "系统异常，请稍后再试", 'data': {'error': str(e)}}

    @classmethod
    def look_ppt_templates(cls, **kwargs: dict) -> dict:
        """
        查看 PPT 模板
        :param kwargs: 包含  page, per_page 的参数字典
        :return: 结果字典
        """
        try:
            from flask import g
            user_id = g.user['user_id']
            page = int(kwargs.get('page', 1))  # 默认第1页
            per_page = int(kwargs.get('per_page', 10))  # 默认每页10条

            if not user_id:
                return {'code': RET.PARAMERR, 'message': "缺少 user_id 参数", 'data': {}}

            # 查询用户模板和系统模板（未删除）
            query = Template.query.filter(
                db.or_(
                    Template.user_id == user_id,
                    Template.is_system == 1
                ),
                Template.is_deleted == 0
            )

            # 分页
            paginated_result = query.paginate(page=page, per_page=per_page, error_out=False)
            templates = paginated_result.items

            # 构造模板数据
            template_list = []
            for tpl in templates:
                # 从路径提取模板名称，确保兼容路径格式
                # template_name = os.path.basename(tpl.location).replace('.pptx', '')

                # 添加模板数据到列表
                template_list.append({
                    "template_id": tpl.template_id,
                    "template_name": tpl.template_name,
                    "user_id": tpl.user_id,
                    "cover_pic": tpl.cover_pic,
                    "location": tpl.location,  # 返回相对路径
                    "is_system": tpl.is_system,
                })

            # 返回结果
            return {
                'code': RET.OK,
                'message': "查询成功",
                'data': {
                    "templates": template_list,
                    "page": paginated_result.page,
                    "total_pages": paginated_result.pages,
                    "total_items": paginated_result.total
                }
            }
        except Exception as e:
            loggings.exception(1, f"Error fetching resources: {e}")
            return {'code': RET.DATAERR, 'message': "系统异常，请稍后再试", 'data': {'error': str(e)}}

    @classmethod
    def add_ppt_template2(cls, **kwargs: dict) -> dict:
        """
        添加 PPT 模板，自动提取第一页作为封面图（cover_pic）
        :param kwargs: 包含 user_id, template_name, file 的参数字典
        :return: 结果字典
        """
        try:
            user_id = kwargs.get('user_id')
            template_name = kwargs.get('template_name')
            ppt_file: FileStorage = kwargs.get('file')

            if not user_id or not template_name or not ppt_file:
                return {'code': RET.PARAMERR, 'message': "缺少必要参数", 'data': {}}

            # 创建目录
            user_template_dir = os.path.join(cls.upload_base, str(user_id))
            os.makedirs(user_template_dir, exist_ok=True)

            # 生成唯一模板ID及保存路径
            template_id = GenerateID.create_random_id()
            ppt_relative_path = os.path.join(user_template_dir, f"{template_id}.pptx")
            ppt_file.save(ppt_relative_path)

            # 提取 PPT 第一页并保存为封面图片
            cover_pic_path = os.path.join(user_template_dir, f"{template_id}_cover.png")
            try:
                prs = Presentation(ppt_relative_path)
                first_slide = prs.slides[0]

                # 渲染第一张幻灯片为图片
                image_stream = cls._render_slide_to_image(first_slide, ppt_relative_path)
                if image_stream:
                    with open(cover_pic_path, 'wb') as f:
                        f.write(image_stream.getvalue())
                else:
                    cover_pic_path = ""  # 如果未能生成封面图，则置空
            except Exception as e:
                loggings.exception(f"Error rendering cover image: {e}")
                cover_pic_path = ""

            # 插入数据库记录
            new_template = Template(
                template_id=template_id,
                user_id=user_id,
                cover_pic=cover_pic_path,  # 存储封面图片路径
                location=ppt_relative_path,  # PPT 文件路径
                is_system=0,
                is_deleted=0,
            )
            db.session.add(new_template)
            db.session.commit()

            return {'code': RET.OK, 'message': "模板添加成功", 'data': {'template_id': template_id}}

        except Exception as e:
            loggings.exception(f"Error in add_ppt_template: {e}")
            db.session.rollback()
            return {'code': RET.SYSERR, 'message': "系统异常，请稍后再试", 'data': {'error': str(e)}}

    @staticmethod
    def _render_slide_to_image(slide, ppt_path: str) -> BytesIO:
        """
        渲染幻灯片为图片
        :param slide: pptx.slide.Slide 对象
        :param ppt_path: PPT 文件路径，用于生成 PDF 中间文件
        :return: 包含图片的 BytesIO 对象
        """
        try:
            # 将 PPTX 转换为 PDF，然后提取第一页
            pdf_path = ppt_path.replace(".pptx", ".pdf")
            os.system(f'libreoffice --headless --convert-to pdf {ppt_path} --outdir {os.path.dirname(ppt_path)}')

            # 使用 pdf2image 提取 PDF 第一页
            images = convert_from_path(pdf_path, first_page=1, last_page=1)
            if images:
                image = images[0]
                img_stream = BytesIO()
                image.save(img_stream, format="PNG")
                return img_stream

        except Exception as e:
            loggings.exception(f"Error in _render_slide_to_image: {e}")
            return None

    @classmethod
    def get_ppt_template(cls, **kwargs: dict):
        """
        获取 PPT 模板文件流
        :param kwargs: 包含 location 的参数字典，location 为文件路径
        :return: 文件流或错误信息
        """
        try:
            # 获取文件路径
            file_path = kwargs.get('location')
            if not file_path:
                return {'code': RET.PARAMERR, 'message': "缺少 location 参数", 'data': {}}

            # 将路径中的反斜杠（\）替换为斜杠（/），确保跨平台兼容性
            file_path = file_path.replace("\\", "/")

            # 构造文件的绝对路径
            absolute_path = os.path.join(cls.base_dir, file_path)

            # 打印路径以调试
            current_app.logger.info(f"Fetching PPT template from: {absolute_path}")

            # 检查文件是否存在
            if not os.path.exists(absolute_path):
                return {'code': RET.NODATA, 'message': "文件不存在", 'data': {}}

            # 检查文件是否为 PPT 文件（扩展名为 .ppt 或 .pptx）
            if not absolute_path.lower().endswith(('.ppt', '.pptx')):
                return {'code': RET.PARAMERR, 'message': "文件格式不支持，仅支持 .ppt 或 .pptx 文件", 'data': {}}

            # 返回文件流
            return send_file(
                absolute_path,
                as_attachment=False,  # 不强制下载，直接展示
                mimetype='application/vnd.ms-powerpoint' if absolute_path.lower().endswith(
                    '.ppt') else 'application/vnd.openxmlformats-officedocument.presentationml.presentation'
            )
        except Exception as e:
            current_app.logger.error(f"Error fetching PPT template: {e}")
            return {'code': RET.DATAERR, 'message': "系统异常，请稍后再试", 'data': {'error': str(e)}}
